#ifndef DECODING_H
#define DECODING_H

#include "decoding_global.h"
#include <iostream>
#include <fstream>
#include <locale>
#include <vector>
#include <cmath>

using namespace std;


namespace approx {
vector<double> DECODING_EXPORT least_squares(vector<double> *x_values, vector<double> *y_values, int max_pow, int thr_count = 0);
vector<vector<double>> DECODING_EXPORT lineal_pices(vector<double> *x_values, vector<double> *y_values);
vector<double> DECODING_EXPORT legendre_approx(vector<double> *x_values, vector<double> *y_values, int max_pow);
double DECODING_EXPORT squared_deviation(vector<double> *v);
double DECODING_EXPORT correlation(vector<double> *x_values, vector<double> *y_values, vector<double> *polynom_values);
double DECODING_EXPORT approx_mistake(vector<double> *x_values, vector<double> *y_values, vector<double> *polynom_values);
vector<double> DECODING_EXPORT calc_polynom_func(vector<double> *polynom_values, vector<double> *x_values);

/*!
 * \brief calc_polynom_point вычисляет значение по функции(полиному)
 *
 * \param poly массив коеффициентов при каждой степени полинома, начиная с нулевой
 * \param size размер массива poly
 * \param point подставляемое значение
 * \return возвращает результат подстановки
 */
double DECODING_EXPORT calc_polynom_point(double *poly, int size, double point);
/*!
 * \brief calc_lineal_point вычисляет значение по кусочно-линейной функции
 *
 * \param koefs массив пар коеффициентов кусочно-линейной функции
 * \param borders массив содержащий максимальные значения, задающие ОДЗ для каждой пары из koefs
 * \param borders_count количество элементов массива borders(в массиве koefs значений будет в 2 раза больше)
 * \return возвращает результат подстановки
 */
double calc_lineal_point(double *koefs, double *borders, int borders_count, double point);
}


class Approx;

/*!
 * \brief Poly_data структура, используемая для хранения данных о полиноме: ошибки аппроксимации mistake,
 * массив коэффициентов полинома poly(где индекс соответствует степени) и количества этих коэффициентов size
 */
typedef struct Poly_data{
    double mistake;
    int size;
    double *poly;

    /*!
     * \brief copy_poly копирует данные из poly во входной массив
     *
     * \param output входной массив
     * \return возвращает размер массива poly
     */
    int copy_poly(double *output);
}Poly_data;

/*!
 * \brief Additional_data структура, используемая для хранения данных о разбиении границ кусочно-линейной аппроксимации. Является необязательной,
 */
typedef struct Additional_data{
    double slise_size;
    int slise_count;
    int *slises;
}Additional_data;

/*!
 * \brief Lineal_data структура, используемая для хранения данных о кусочно-линейной аппроксимации: пары коеффициентов koefs и кол-во этих пар, границы действия коеффициентов borders
 */
typedef struct Lineal_data{
public:
    int pair_count;
    double *koefs;
    double *borders;
    Additional_data *ad_data;

    Lineal_data();

    /*!
     * \brief fill_ad_data копирует данные из poly во входной массив
     */
    void fill_ad_data();
    /*!
     * \brief copy_to_borders копирует данные в borders из входного массива
     *
     * \param values входной массив
     */
    void copy_to_borders(double *values, int count);

private:
    /*!
     * \brief slise_size вычисляет lineal_data->ad_data->slise_size
     */
    void slise_size();
    /*!
     * \brief slise_count вычисляет lineal_data->ad_data->slise_count
     */
    void slise_count();
    /*!
     * \brief lineal_split вычисляет lineal_data->ad_data->slises
     */
    void lineal_split();

}Lineal_data;

/*!
 * \brief Approx class предназначен для расчёта аппроксимации и для хранения и обработки результата аппроксимации
*/
class DECODING_EXPORT Approx {

private:
    Poly_data *poly_data;
    Lineal_data *lineal_data;

public:
    Approx();

    /*!
     * \brief approx_mistake расчитывает ошибку аппроксимации в процентах если полином занесён в структуру poly_data.
     * За 100% берётся разница между максимальным и минимальным y
     *
     * \param x_values массив значений x узлов интерполяции
     * \param y_values массив значений y узлов интерполяции
     * \param gh_size кол-во узлов интерполяции
     */
    void approx_mistake(double *x_values, double *y_values, int gh_size);
    /*!
     * \brief approx_mistake расчитывает ошибку аппроксимации в процентах если полином занесён в структуру poly_data.
     *  За 100% берётся разница между значением y и нулём. Нулевые значения не учитываются в расчёте
     *
     * \param x_values массив значений x узлов интерполяции
     * \param y_values массив значений y узлов интерполяции
     * \param gh_size кол-во узлов интерполяции
     */
    void approx_mistake_bigzero(double *x_values, double *y_values, int gh_size);
    /*!
     * \brief least_squares_arr расчитывает полином методом наименьших квадратов с заданной степенью. Заполняет структуру poly_data
     *
     * \param x_values массив значений x узлов интерполяции
     * \param y_values массив значений y узлов интерполяции
     * \param input_size кол-во узлов интерполяции
     * \param output_pow степень полинома, который будет расчитан
     */
    void least_squares_arr(double *x_values, double *y_values, int input_size, int output_pow);
    /*!
     * \brief least_squares_auto_pow расчитывает полином методом наименьших квадратов, подбирая оптимальную степень(не выше 7). Заполняет структуру poly_data
     *
     * \param x_values массив значений x узлов интерполяции
     * \param y_values массив значений y узлов интерполяции
     * \param input_size кол-во узлов интерполяции
     */
    void least_squares_auto_pow(double *x_values, double *y_values, int input_size);
    /*!
     * \brief legendre_approx_arr расчитывает полином методом ортогональных полиномов Лежандра. Заполняет структуру poly_data
     *
     * \param x_values массив значений x узлов интерполяции
     * \param y_values массив значений y узлов интерполяции
     * \param input_size кол-во узлов интерполяции
     * \param output_pow степень полинома, который будет расчитан
     */
    void legendre_approx_arr(double *x_values, double *y_values, int input_size, int output_pow);
    /*!
     * \brief legendre_approx_auto_pow расчитывает полином методом ортогональных полиномов Лежандра, подбирая оптимальную степень(не выше 7). Заполняет структуру poly_data
     *
     * \param x_values массив значений x узлов интерполяции
     * \param y_values массив значений y узлов интерполяции
     * \param input_size кол-во узлов интерполяции
     */
    void legendre_approx_auto_pow(double *x_values, double *y_values, int input_size);
    /*!
     * \brief lineal_pices расчитывает коэффициенты методом кусочно-линейной аппроксимации. Заполняет структуру lineal_data
     *
     * \param x_values массив значений x узлов интерполяции
     * \param y_values массив значений y узлов интерполяции
     * \param input_size кол-во узлов интерполяции
     */
    void lineal_pices(double *x_values, double *y_values, int input_size);
    /*!
     * \brief calc_lineal_point расчитывает значение кусочно-линейной функции в заданной точке
     *
     * \param point точка для расчёта функции
     * \return возвращает расчитанное значение кусочно-линейной функции
     */
    template <typename Ty>
    double calc_lineal_point(Ty point);
    /*!
     * \brief calc_lineal_pices расчитывает значения кусочно-линейной функции в заданных точках
     *
     * \param x_values массив точек для расчёта функции
     * \param output массив в который будут записаны расчитанные значения
     * \param x_size размер массивов x_values и output
     */
    void calc_lineal_pices(double *x_values, double *output, int x_size);
    /*!
     * \brief calc_split_lineal_point расчитывает значение кусочно-линейной функции в заданной точке, предварительно заполнив структуру
     * lineal_data->ad_data для ускоренных вычислений(в случае необходимасти большого количества вызовов данного метода для данной кусочно-линейной функции)
     *
     * \param point точка для расчёта функции
     * \return возвращает расчитанное значение кусочно-линейной функции
     */
    template <typename Ty>
    double calc_split_lineal_point(Ty point);
    /*!
     * \brief calc_split_lineal расчитывает значения кусочно-линейной функции в заданных точках, предварительно заполнив структуру lineal_data->ad_data для ускоренных вычислений
     *
     * \param x_values массив точек для расчёта функции
     * \param output массив в который будут записаны расчитанные значения
     * \param x_size размер массивов x_values и output
     */
    void calc_split_lineal(double *x_values, double *output, int x_size);
    /*!
     * \brief calc_polynom_func расчитывает значения функции(полинома) в заданных точках
     *
     * \param x_values массив точек для расчёта функции
     * \param output массив в который будут записаны расчитанные значения
     * \param x_size размер массивов x_values и output
     */
    void calc_polynom_func(double *x_values, double *output, int x_size);
    /*!
     * \brief calc_polynom_point расчитывает значение функции(полинома) в заданной точке
     *
     * \param point точка для расчёта функции
     * \return возвращает расчитанное значение функции(полинома)
     */
    template <typename Ty>
    double calc_polynom_point(Ty x);
    /*!
     * \brief get_poly записывает полином poly_data->poly во входной массив
     *
     * \param output массив в который будут записаны значения полинома
     */
    void get_poly(double *output);
    /*!
     * \brief get_poly_size возвращает размер массива коеффициентов полинома poly_data->size
     *
     * \return возвращает размер массива коеффициентов полинома
     */
    int get_poly_size();
    /*!
     * \brief get_mistake возвращает ошибку аппроксимации poly_data->mistake
     *
     * \return возвращает ошибку аппроксимации
     */
    double get_mistake();
    /*!
     * \brief set_poly заполняет структуру poly_data входными параметрами
     *
     * \param poly массив коеффициентов полинома(где индекс соответствует степени), значения которого будут записаны в структуру
     * \param size размер массива poly
     * \param mistake ошибка аппроксимации
     */
    void set_poly(double *poly, int size, double mistake = -1);
    /*!
     * \brief get_koefs заполняет входной массив парами коеффициентов кусочно-линейной аппроксимации lineal_data->koefs
     *
     * \param output массив в который будут записаны пары коеффициентов. На чётных индексах k0, на нечётных k1
     */
    void get_koefs(double *output);
    /*!
     * \brief set_koefs заполняет структуру lineal_data входными параметрами
     *
     * \param koefs массив пар коеффициентов кусочно-линейной аппроксимации. На чётных индексах k0, на нечётных k1
     * \param borders массив, в котором хранятся границы действия коеффициентов koefs
     * \param borders_count размер массива borders
     */
    void set_koefs(double *koefs, double *borders, int borders_count);
};


namespace MODGH {

void DECODING_EXPORT x5(double *input_output_x, int x_size, double k1, double k2, double k3);

void DECODING_EXPORT x6(double *input_output_x, int x_size, double c1, double c2, double c3);

void DECODING_EXPORT x7(double *input_output_x, int x_size, double g1, double g2, double g3);

void DECODING_EXPORT y1(double *input_output_y, int y_size, double Kp);

void DECODING_EXPORT y2(double *input_output_y, int y_size, double Dp);

void DECODING_EXPORT y3(double *input_output_y, int y_size, double nn);

void DECODING_EXPORT y4(double *input_output_y, int y_size, double d, double k);

void DECODING_EXPORT x1(double *input_output_x, int x_size, double Ub);

void DECODING_EXPORT y5(double *output_2x, double *output_2y, double U0, double Ub, double k);

void DECODING_EXPORT xy5(double *output_30x, double *output_30y, double L, double r, double R);

void DECODING_EXPORT xy6(double *output_2x, double *output_2y, double k, double Dn, double Dk, double U0, double Ub);

void DECODING_EXPORT x4(double *input_output_x, int x_size, double d, double k);

void DECODING_EXPORT x3(double *input_df, double *output_x, int gh_size, double f0);

void DECODING_EXPORT x2(double *input_output_x, int x_size, double u1, double u2, double v1, double v2, double Ub);

void DECODING_EXPORT xy7(double *input_x1, double *input_y1, int gh1_size, double *input_x2, double *input_y2, int gh2_size, double *output_7poly);

void DECODING_EXPORT xy8(double *input_x1, double *input_y1, double *input_x2, double *input_y2, int gh_size, double *output_7poly);
}
#endif // DECODING_H
